<!DOCTYPE html>
<html>
	<head>
	<meta charset="utf-8">
	<meta http-equiv="X-UA-Compatible" content="IE=edge" />
	<meta name="viewport" content="width=device-width, initial-scale=1" />
	<title>密码生成器</title>
		<style>
			.center{
				text-align:center;  /*水平居中*/
			}
			#output{
				height: auto;
				word-wrap:break-word;
				word-break:break-all;
				//overflow: hidden;
			}
			#main {
				height: 100%;
				width: 100%;
				position: absolute;                       
				top: 30px;
				left: 30px;
				z-index: 1;
			}
			#front {
				height: 360px;
				width: 90%;
				background-color: lightblue;
				opacity: 0.9;
				position: absolute;                       
				top: 30px;
				left: 30px;
				z-index: 2;
				display:none
			}
			#text {
				width: 260px;
			}
		</style>
		<script>
			function bitlen(n){
				if(n<=1)return 1;
				var i=0;
				while(n>0){
					i++;
					n=n>>1;
				}
				return i;
			}
			function int2Str(n,len){
				var s=n.toString(16);
				while(s.length<len*2){
					s="0"+s;
				}
				return s;
			}
			function array2hex(u8array){
				var hex = Array.prototype.map.call(u8array, function (e) {
					return (e < 16 ? "0" : "") + e.toString(16);
				}).join("");
				return hex;
			}
			function encodeUTF8(s) {
				var i,
				r = [],
				c,
				x;
				for (i = 0; i < s.length; i++)
					if ((c = s.charCodeAt(i)) < 0x80)
						r.push(c);
					else if (c < 0x800)
						r.push(0xC0 + (c >> 6 & 0x1F), 0x80 + (c & 0x3F));
					else {
						if ((x = c ^ 0xD800) >> 10 == 0)
							c = (x << 10) + (s.charCodeAt(++i) ^ 0xDC00) + 0x10000, r.push(0xF0 + (c >> 18 & 0x7), 0x80 + (c >> 12 & 0x3F));
						else
							r.push(0xE0 + (c >> 12 & 0xF));
						r.push(0x80 + (c >> 6 & 0x3F), 0x80 + (c & 0x3F));
					};
				return r;
			}
			function sha1(s) {
				var data = new Uint8Array(encodeUTF8(s))
					var i,
				j,
				t;
				var l = ((data.length + 8) >>> 6 << 4) + 16,
				s = new Uint8Array(l << 2);
				s.set(new Uint8Array(data.buffer)),
				s = new Uint32Array(s.buffer);
				for (t = new DataView(s.buffer), i = 0; i < l; i++)
					s[i] = t.getUint32(i << 2);
				s[data.length >> 2] |= 0x80 << (24 - (data.length & 3) * 8);
				s[l - 1] = data.length << 3;
				var w = [],
				f = [function () {
						return m[1] & m[2] | ~m[1] & m[3];
					}, function () {
						return m[1] ^ m[2] ^ m[3];
					}, function () {
						return m[1] & m[2] | m[1] & m[3] | m[2] & m[3];
					}, function () {
						return m[1] ^ m[2] ^ m[3];
					}
				],
				rol = function (n, c) {
					return n << c | n >>> (32 - c);
				},
				k = [1518500249, 1859775393, -1894007588, -899497514],
				m = [1732584193, -271733879, null, null, -1009589776];
				m[2] = ~m[0],
				m[3] = ~m[1];
				for (i = 0; i < s.length; i += 16) {
					var o = m.slice(0);
					for (j = 0; j < 80; j++)
						w[j] = j < 16 ? s[i + j] : rol(w[j - 3] ^ w[j - 8] ^ w[j - 14] ^ w[j - 16], 1), t = rol(m[0], 5) + f[j / 20 | 0]() + m[4] + w[j] + k[j / 20 | 0] | 0, m[1] = rol(m[1], 30), m.pop(), m.unshift(t);
					for (j = 0; j < 5; j++)
						m[j] = m[j] + o[j] | 0;
				};
				t = new DataView(new Uint32Array(m).buffer);
				for (var i = 0; i < 5; i++)
					m[i] = t.getUint32(i << 2);
				return new Uint8Array(new Uint32Array(m).buffer);
			}
			var List=function(u8array){
				this.index=0;
				this.buff=u8array;
				this.get=function(){
					if(this.index<this.buff.length){
						return this.buff[this.index++];
					}
					return -1;
				}
			}
			var Sha1Prng =function(seed) {
				  this.seed = seed.substr(0,seed.length*2/3);
				  this.offset = 0;
				  this.last=seed.substr(seed.length*2/3);
				  this.newList=function(){
						var sha=sha1(this.last+this.seed+int2Str(this.offset++,64));
						this.last=array2hex(sha.subarray(sha.length/2));
						return new List(sha.subarray(0,sha.length/2));
				  };
				  this.list=this.newList(),
				  this.getOne= function(max,bitlen) {
						var v;
						do{
							v=this.list.get();
							if(v==-1){
								this.list=this.newList();
							}
							v=v>>(8-bitlen);
						}while(v<0||v>=max);
						return v;
				  }
				  this.getSome=function(target,max,len,counter){
						result = "";
						var bitlength=bitlen(max-1);
						for(var i=0;i<len;i++){
							var v=this.getOne(max,bitlength);
							result+=target.charAt(v);
							counter[v]+=1;
							//if((i&31)==31){
							//	result+="\n";
							//}
						}
						return result;
					  }
				};
			var linkCounterContent=function(target,counter,average){
				var s2=0;
				var s="";
				for(var i=0;i<target.length;i++){
					s+=target.charAt(i)+":"+counter[i]+"\t";
					s2+=(counter[i]-average)*(counter[i]-average);
				}
				return s
				+"\n标准差:"+Math.sqrt(s2/target.length)
				+"\n变异系数:"+Math.sqrt(s2/target.length)/average*100+"%";
			}
			var show = function(){
				var target="";
				if(document.getElementById("a_z").checked){
					target+="abcdefghijklmnopqrstuvwxyz";
				}
				if(document.getElementById("A_Z").checked){
					target+="ABCDEFGHIJKLMNOPQRSTUVWXYZ";
				}
				if(document.getElementById("_0_9").checked){
					target+="0123456789";
				}
				if(document.getElementById("others").checked){
					target+='!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~';
				}
				target+=document.getElementById("text").value;
				target=target.trim();
				if(target.length<1){
					return;
				}
				var count=0,need=513,outNeed=2048;
				var s="";
				var move=function(x,y){
					s+=""+x+y;
					document.getElementById("div2").innerText=(++count)+"/"+need;
					if(count>=need){
						front.style.display="none";
						var sha1prng=new Sha1Prng(s);
						var k=Math.log(target.length)/Math.log(2);
						
						var counter=Array(target.length);
						counter.fill(0,0,counter.length);
						//counter.length=target.length;
						
						var some=sha1prng.getSome(target,target.length,outNeed,counter);
						
						document.getElementById("output").innerText=
						"\n共"+outNeed+"字符\n\n"
						+some
						+("\n\n本次生成密码强度信息:"
						+"\n    每字符约"+k+"位"
						+"\n    128位强度约需要 "+(128/k)+" 个字符"
						+"\n\n各字符计数(平均:"+(outNeed/target.length)+"):\n"
						+linkCounterContent(target,counter,outNeed/target.length));
						return true;
					}
					return false;
				}
				var front=document.getElementById("front");
				front.style.display="block";
				document.getElementById("div2").innerText="0/"+need;
				front.onmousemove = function(evt) {
					evt = window.event || evt;
					move(evt.screenX,evt.screenY);
				}
				var touchmove= function(evt) {
					evt = window.event || evt;
					var finish=move(evt.touches[0].screenX,evt.touches[0].screenY);
					if(finish){
						front.removeEventListener('touchmove',touchmove,false);
					}
					evt.preventDefault();
				};
				front.addEventListener('touchmove',touchmove,false);
			}			
		</script>
	</head>
	<body>
		<div id="main">
			<h3 class="center">密码生成器</h3>
			<input type="checkbox" checked="true" id="a_z"/>a~z&nbsp;&nbsp;
			<input type="checkbox" checked="true" id="A_Z"/>A~Z&nbsp;&nbsp;
			<input type="checkbox" checked="true" id="_0_9"/>0~9&nbsp;&nbsp;
			<input type="checkbox" id="others"/>特殊字符&nbsp;&nbsp;自选字符
			<input type="text" placeholder="不超过128个且不得与前面选择的字符重复" id="text"/><br/>
			<div>
			<div class="center">常用自选字符</div>
			0123456789ABCDEF&nbsp;&nbsp;0123456789abcdef&nbsp;&nbsp;+/&nbsp;&nbsp;!&quot;#$%&amp;&#39;()*+,-./:;&lt;=&gt;?@[\]^_`{|}~
			</div>
			<br/>
			<button type="button" onclick="show()">生成</button>
			<br/>
			<p id="output"></p>
		</div>
		<div id="front">
			<div>请随机滑动鼠标或手指生成密码</div>
			<div id="div2"></div>
		</div>
	</body>
</html>